﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Windows;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using IssueVision.Common;
using IssueVision.EntityModel;

namespace IssueVision.ViewModel
{
    [Export(ViewModelTypes.IssueEditorViewModel, typeof(ViewModelBase))]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class IssueEditorViewModel : ViewModelBase
    {
        #region "Private Data Members"
        private IIssueVisionModel _issueVisionModel;
        private Issue _currentIssueCache;
        private static readonly int MaxFileQuota = 20971520; 
        #endregion "Private Data Members"

        #region "Constructor"
        [ImportingConstructor]
        public IssueEditorViewModel(IIssueVisionModel issueVisionModel)
        {
            _issueVisionModel = issueVisionModel;

            // set up event handling
            _issueVisionModel.GetIssueTypesCompleted += _issueVisionModel_GetIssueTypesCompleted;
            _issueVisionModel.GetPlatformsCompleted += _issueVisionModel_GetPlatformsCompleted;
            _issueVisionModel.GetResolutionsCompleted += _issueVisionModel_GetResolutionsCompleted;
            _issueVisionModel.GetStatusesCompleted += _issueVisionModel_GetStatusesCompleted;
            _issueVisionModel.GetSubStatusesCompleted += _issueVisionModel_GetSubStatusesCompleted;
            _issueVisionModel.GetUsersCompleted += _issueVisionModel_GetUsersCompleted;

            // set _currentIssueCache to null
            _currentIssueCache = null;

            // load issue type entries
            IssueTypeEntries = null;
            _issueVisionModel.GetIssueTypesAsync();
            // load platform entries
            PlatformEntries = null;
            _issueVisionModel.GetPlatformsAsync();
            //load resolution entries
            ResolutionEntriesWithNull = null;
            _issueVisionModel.GetResolutionsAsync();
            // load status entries
            StatusEntries = null;
            _issueVisionModel.GetStatusesAsync();
            // load substatus entries
            SubstatusEntriesWithNull = null;
            _issueVisionModel.GetSubStatusesAsync();
            // load user entries
            UserEntries = null;
            UserEntriesWithNull = null;
            _issueVisionModel.GetUsersAsync();

            // register for EditIssueMessage
            AppMessages.EditIssueMessage.Register(this, OnEditIssueMessage);
        }
        #endregion "Constructor"

        #region "ICleanup interface implementation"
        public override void Cleanup()
        {
            if (_issueVisionModel != null)
            {
                // unregister all events
                _issueVisionModel.GetIssueTypesCompleted -= _issueVisionModel_GetIssueTypesCompleted;
                _issueVisionModel.GetPlatformsCompleted -= _issueVisionModel_GetPlatformsCompleted;
                _issueVisionModel.GetResolutionsCompleted -= _issueVisionModel_GetResolutionsCompleted;
                _issueVisionModel.GetStatusesCompleted -= _issueVisionModel_GetStatusesCompleted;
                _issueVisionModel.GetSubStatusesCompleted -= _issueVisionModel_GetSubStatusesCompleted;
                _issueVisionModel.GetUsersCompleted -= _issueVisionModel_GetUsersCompleted;
                // set CurrentIssue back to null
                CurrentIssue = null;
                // set _issueVisionModel to null
                _issueVisionModel = null;
            }
            // set properties back to null
            IssueTypeEntries = null;
            PlatformEntries = null;
            ResolutionEntriesWithNull = null;
            StatusEntries = null;
            SubstatusEntriesWithNull = null;
            UserEntries = null;
            UserEntriesWithNull = null;
            // unregister any messages for this ViewModel
            base.Cleanup();
        }
        #endregion "ICleanup interface implementation"

        #region "Public Properties"

        private Issue _currentIssue;

        public Issue CurrentIssue
        {
            get { return _currentIssue; }
            private set
            {
                if (!ReferenceEquals(_currentIssue, value))
                {
                    _currentIssue = value;
                    RaisePropertyChanged("CurrentIssue");
                }
            }
        }

        private IEnumerable<byte> _priorityEntries;

        public IEnumerable<byte> PriorityEntries
        {
            get
            {
                if (_priorityEntries == null)
                {
                    List<byte> _priorityList = new List<byte>();
                    for (byte i = IssueVisionServiceConstant.HighestPriority; i <= IssueVisionServiceConstant.LowestPriority; i++)
                    {
                        _priorityList.Add(i);
                    }
                    _priorityEntries = _priorityList.AsEnumerable();
                }
                return _priorityEntries;
            }
        }

        private IEnumerable<byte> _severityEntries;

        public IEnumerable<byte> SeverityEntries
        {
            get
            {
                if (_severityEntries == null)
                {
                    List<byte> _severityList = new List<byte>();
                    for (byte i = IssueVisionServiceConstant.HighestSeverity; i <= IssueVisionServiceConstant.LowestSeverity; i++)
                    {
                        _severityList.Add(i);
                    }
                    _severityEntries = _severityList.AsEnumerable();
                }
                return _severityEntries;
            }
        }

        private IEnumerable<IssueType> _issueTypeEntries;

        public IEnumerable<IssueType> IssueTypeEntries
        {
            get { return _issueTypeEntries; }
            private set
            {
                if (!ReferenceEquals(_issueTypeEntries, value))
                {
                    _issueTypeEntries = value;
                    RaisePropertyChanged("IssueTypeEntries");
                }
            }
        }

        private IEnumerable<Platform> _platformEntries;

        public IEnumerable<Platform> PlatformEntries
        {
            get { return _platformEntries; }
            private set
            {
                if (!ReferenceEquals(_platformEntries, value))
                {
                    _platformEntries = value;
                    RaisePropertyChanged("PlatformEntries");
                }
            }
        }

        private IEnumerable<Resolution> _resolutionEntriesWithNull;

        public IEnumerable<Resolution> ResolutionEntriesWithNull
        {
            get { return _resolutionEntriesWithNull; }
            private set
            {
                if (!ReferenceEquals(_resolutionEntriesWithNull, value))
                {
                    _resolutionEntriesWithNull = value;
                    RaisePropertyChanged("ResolutionEntriesWithNull");
                }
            }
        }

        private IEnumerable<Status> _statusEntries;

        public IEnumerable<Status> StatusEntries
        {
            get { return _statusEntries; }
            private set
            {
                if (!ReferenceEquals(_statusEntries, value))
                {
                    _statusEntries = value;
                    RaisePropertyChanged("StatusEntries");
                }
            }
        }

        private IEnumerable<SubStatus> _substatusEntriesWithNull;

        public IEnumerable<SubStatus> SubstatusEntriesWithNull
        {
            get { return _substatusEntriesWithNull; }
            private set
            {
                if (!ReferenceEquals(_substatusEntriesWithNull, value))
                {
                    _substatusEntriesWithNull = value;
                    RaisePropertyChanged("SubstatusEntriesWithNull");
                }
            }
        }

        private IEnumerable<User> _userEntries;

        public IEnumerable<User> UserEntries
        {
            get { return _userEntries; }
            private set
            {
                if (!ReferenceEquals(_userEntries, value))
                {
                    _userEntries = value;
                    RaisePropertyChanged("UserEntries");
                }
            }
        }

        private IEnumerable<User> _userEntriesWithNull;

        public IEnumerable<User> UserEntriesWithNull
        {
            get { return _userEntriesWithNull; }
            private set
            {
                if (!ReferenceEquals(_userEntriesWithNull, value))
                {
                    _userEntriesWithNull = value;
                    RaisePropertyChanged("UserEntriesWithNull");
                }
            }
        }

        #endregion "Public Properties"

        #region "Public Commands"

        private RelayCommand _addFileCommand;

        public RelayCommand AddFileCommand
        {
            get
            {
                if (_addFileCommand == null)
                {
                    _addFileCommand = new RelayCommand(
                        () => OnAddFileCommand(),
                        () => CurrentIssue != null);
                }
                return _addFileCommand;
            }
        }

        private void OnAddFileCommand()
        {
            try
            {
                if (!_issueVisionModel.IsBusy)
                {
                    FileInfo addedFile = null;
                    // ask user for a file to add
                    AppMessages.OpenFileMessage.Send(new NotificationMessageAction<FileInfo>("Open File", g => addedFile = g));

                    if (addedFile != null)
                    {
                        using (var fs = addedFile.OpenRead())
                        using (BinaryReader br = new BinaryReader(fs))
                        {
                            int currentQuota = CurrentIssue.Files.Sum(n => n.Data.Length)
                                + (int)fs.Length;

                            if (currentQuota < MaxFileQuota)
                            {
                                // and then add the file into the Files entity collection
                                CurrentIssue.Files.Add(
                                    new EntityModel.File
                                        {
                                            FileID = Guid.NewGuid(),
                                            FileName = addedFile.Name,
                                            Data = br.ReadBytes((int)fs.Length)
                                        });
                            }
                            else
                            {
                                // notify user that max file quota has reached
                                DialogMessage dialogMessage = new DialogMessage(
                                    this,
                                    CommonResources.MaxFileQuotaText,
                                    null)
                                {
                                    Button = MessageBoxButton.OK,
                                    Caption = CommonResources.MaxFileQuotaCaption
                                };

                                AppMessages.StatusUpdateMessage.Send(dialogMessage);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(ex);
            }
        }

        private RelayCommand<EntityModel.File> _removeFileCommand;

        public RelayCommand<EntityModel.File> RemoveFileCommand
        {
            get
            {
                if (_removeFileCommand == null)
                {
                    _removeFileCommand = new RelayCommand<EntityModel.File>(
                        g => OnRemoveFileCommand(g),
                        g => g != null);
                }
                return _removeFileCommand;
            }
        }

        private void OnRemoveFileCommand(EntityModel.File g)
        {
            try
            {
                if (!_issueVisionModel.IsBusy)
                {
                    CurrentIssue.Files.Remove(g);
                    g.MarkAsDeleted();
                }
            }
            catch (Exception ex)
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(ex);
            }
        }

        private RelayCommand<EntityModel.File> _saveToDiskCommand;

        public RelayCommand<EntityModel.File> SaveToDiskCommand
        {
            get
            {
                if (_saveToDiskCommand == null)
                {
                    _saveToDiskCommand = new RelayCommand<EntityModel.File>(g =>
                    {
                        if (!_issueVisionModel.IsBusy)
                        {
                            AppMessages.SaveFileMessage.Send(g);
                        }
                    }, g => g != null);
                }
                return _saveToDiskCommand;
            }
        }

        private RelayCommand _addAttributeCommand;

        public RelayCommand AddAttributeCommand
        {
            get
            {
                if (_addAttributeCommand == null)
                {
                    _addAttributeCommand = new RelayCommand(
                        () => OnAddAttributeCommand(),
                        () => CurrentIssue != null);
                }
                return _addAttributeCommand;
            }
        }

        private void OnAddAttributeCommand()
        {
            if (!_issueVisionModel.IsBusy)
            {
                CurrentIssue.Attributes.Add(
                    new EntityModel.Attribute
                        {
                            ID = Guid.NewGuid(),
                            AttributeName = "Key",
                            Value = "Value"
                        });
            }
        }

        private RelayCommand<EntityModel.Attribute> _removeAttributeCommand;

        public RelayCommand<EntityModel.Attribute> RemoveAttributeCommand
        {
            get
            {
                if (_removeAttributeCommand == null)
                {
                    _removeAttributeCommand = new RelayCommand<EntityModel.Attribute>(g =>
                    {
                        if (!_issueVisionModel.IsBusy)
                        {
                            CurrentIssue.Attributes.Remove(g);
                            g.MarkAsDeleted();
                        }
                    }, g => g != null);
                }
                return _removeAttributeCommand;
            }
        }

        #endregion "Public Commands"

        #region "Private Methods"

        /// <summary>
        /// Event handler for GetIssueTypesCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _issueVisionModel_GetIssueTypesCompleted(object sender, ResultsArgs<IssueType> e)
        {
            if (!e.HasError)
            {
                IssueTypeEntries = e.Results.OrderBy(g => g.Name);
                // check whether IssueTypeEntries is populated after CurrentIssue
                AssignCurrentIssue(null);
            }
            else
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        /// <summary>
        /// Event handler for GetPlatformsCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _issueVisionModel_GetPlatformsCompleted(object sender, ResultsArgs<Platform> e)
        {
            if (!e.HasError)
            {
                PlatformEntries = e.Results.OrderBy(g => g.OS).ThenBy(g => g.OSVersion);
                // check whether PlatformEntries is populated after CurrentIssue
                AssignCurrentIssue(null);
            }
            else
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        /// <summary>
        /// Event handler for GetResolutionsCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _issueVisionModel_GetResolutionsCompleted(object sender, ResultsArgs<Resolution> e)
        {
            if (!e.HasError)
            {
                // create a new resolution list with the first item of null Name
                List<Resolution> resolutionList = e.Results.ToList();
                resolutionList.Add(new Resolution());
                ResolutionEntriesWithNull = resolutionList.OrderBy(g => g.Name).AsEnumerable();
                // check whether ResolutionEntriesWithNull is populated after CurrentIssue
                AssignCurrentIssue(null);
            }
            else
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        /// <summary>
        /// Event handler for GetStatusesCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _issueVisionModel_GetStatusesCompleted(object sender, ResultsArgs<Status> e)
        {
            if (!e.HasError)
            {
                StatusEntries = e.Results.OrderBy(g => g.StatusID);
                // check whether StatusEntries is populated after CurrentIssue
                AssignCurrentIssue(null);
            }
            else
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        /// <summary>
        /// Event handler for GetSubStatusesCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _issueVisionModel_GetSubStatusesCompleted(object sender, ResultsArgs<SubStatus> e)
        {
            if (!e.HasError)
            {
                // create a new substatus list with the first item of null Name
                List<SubStatus> substatusList = e.Results.ToList();
                substatusList.Add(new SubStatus());
                SubstatusEntriesWithNull = substatusList.OrderBy(g => g.Name).AsEnumerable();
                // check whether SubstatusEntriesWithNull is populated after CurrentIssue
                AssignCurrentIssue(null);
            }
            else
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        /// <summary>
        /// Event handler for GetUsersCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _issueVisionModel_GetUsersCompleted(object sender, ResultsArgs<User> e)
        {
            if (!e.HasError)
            {
                UserEntries = e.Results.OrderBy(g => g.Name);
                // create a new user list with the first item of null user name
                List<User> userList = e.Results.ToList();
                userList.Add(new User());
                UserEntriesWithNull = userList.OrderBy(g => g.Name).AsEnumerable();
                // check whether UserEntries/UserEntriesWithNull is populated after CurrentIssue
                AssignCurrentIssue(null);
            }
            else
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        /// <summary>
        /// Function for EditIssueMessage
        /// </summary>
        /// <param name="editIssue"></param>
        private void OnEditIssueMessage(Issue editIssue)
        {
            if (editIssue != null)
            {
                // check whether this issue is read-only or not
                AppMessages.ReadOnlyIssueMessage.Send(editIssue.IsIssueReadOnly());

                AssignCurrentIssue(editIssue);
            }
            else
            {
                // this happens only if there is no matching issue to edit
                CurrentIssue = editIssue;
            }
        }

        /// <summary>
        /// Assign edit issue only after all ComboBox are ready
        /// </summary>
        /// <param name="editIssue"></param>
        private void AssignCurrentIssue(Issue editIssue)
        {
            if (editIssue != null)
            {
                // this call is coming from OnEditIssueMessage()
                if (IssueTypeEntries != null && PlatformEntries != null &&
                    ResolutionEntriesWithNull != null && StatusEntries != null &&
                    SubstatusEntriesWithNull != null && UserEntries != null &&
                    UserEntriesWithNull != null)
                {
                    // if all ComboBox are ready, we set CurrentIssue
                    CurrentIssue = editIssue;
                    _currentIssueCache = null;
                }
                else
                    _currentIssueCache = editIssue;
            }
            else
            {
                // this call is coming from one of the completed event handlers
                if (_currentIssueCache != null && IssueTypeEntries != null &&
                    PlatformEntries != null && ResolutionEntriesWithNull != null &&
                    StatusEntries != null && SubstatusEntriesWithNull != null &&
                    UserEntries != null && UserEntriesWithNull != null)
                {
                    // if all ComboBox are ready, we set CurrentIssue
                    CurrentIssue = _currentIssueCache;
                    _currentIssueCache = null;
                }
            }
        }

        #endregion "Private Methods"
    }
}